Ritorna all'inizio


Domini funzionali e cartesiani

Un dominio è, nella pratica della semantica denotazionale, una CPO⊥: un ordine parziale completo dotato di elemento minimo ⊥. Questo articolo dà per acquisita la teoria degli ordini, poset, catene e lub, CPO e CPO⊥, funzioni monotone e continue, Teorema di Kleene, e si concentra su un'altra domanda: come si costruiscono i domini in modo compositivo, e perché le funzioni che vi operano risultano continue.

La motivazione è dare semantica denotazionale a un linguaggio funzionale tipato (l'esempio di riferimento è HOFL, Higher-Order Functional Language). Servono allora non un dominio, ma una famiglia intera: un dominio Dτ per ciascun tipo τ::=int∣τ0*τ1∣τ0→τ1 in modo che ogni termine t:τ denoti un valore [[t]]ρ∈Dτ (dove ρ è un ambiente type-sensitive che assegna alle variabili valori del dominio giusto). Inoltre, poiché HOFL ha la ricorsione recx.t, il significato si ottiene risolvendo un'equazione ricorsiva tramite fix, e quindi ogni Dτ deve essere una CPO⊥ e ogni funzionale coinvolto deve essere continuo.

La strategia è compositiva: si fissa il dominio base Dint e si forniscono dei costruttori che, dati Dτ0 e Dτ1, producono Dτ0*τ1 e Dτ0→τ1. Il requisito cruciale è che ogni costruttore preservi la struttura di CPO⊥, e che le operazioni associate (proiezioni, applicazione, ricorsione…) siano continue.

Il dominio piatto degli interi

Il tipo base si interpreta nel dominio piatto Dint=ℤ⊥: gli interi più un ⊥ che rappresenta la non terminazione. Essendo un ordine piatto è automaticamente una CPO⊥ (ha bottom e ammette solo catene finite).

Le operazioni aritmetiche op∈{+,×} si sollevano al dominio piatto come estensioni strette op⊥:ℤ⊥×ℤ⊥→ℤ⊥: il risultato v1mathbinop⊥v2 vale v1mathbinopv2 quando entrambi gli argomenti sono interi (v1,v2∈ℤ), e vale ⊥ altrimenti (cioè non appena v1=⊥ oppure v2=⊥).

"Stretta" significa che ⊥ si propaga: se un argomento diverge, diverge il risultato. Queste funzioni sono monotone e continue, automaticamente, poiché il loro dominio ℤ⊥×ℤ⊥ ammette solo catene finite (vale il lemma "niente catene infinite ⇒ monotona = continua").

Prodotto cartesiano di domini

Date due CPO⊥ D ed E, il prodotto cartesiano D×E si ordina componente per componente: (d0,e0)⊑D×E(d1,e1)⇔d0⊑Dd1∧e0⊑Ee1

Si verifica che D×E è ancora una CPO⊥:

⨆i∈ℕ(di,ei)=(⨆i∈ℕdi,⨆i∈ℕei) È un maggiorante (ogni componente lo è) ed è il minimo dei maggioranti (se (d,e) maggiora la catena, allora d maggiora {di} ed e maggiora {ei}, da cui la disuguaglianza componente per componente).

Le proiezioni π1(d,e)=d e π2(d,e)=e sono monotone e continue. La continuità di π1, ad esempio, è immediata dalla definizione di lub di coppie: π1(⨆i(di,ei))=π1(⨆idi,⨆iei)=⨆idi=⨆iπ1(di,ei)

Variante: prodotto smashed. Talvolta si usa il prodotto "schiacciato" D⊗E, che identifica in un unico bottom tutte le coppie con almeno una componente indefinita. È un buon esercizio verificare che anch'esso è una CPO⊥.

Switch lemma

Prima di affrontare lo spazio delle funzioni serve uno strumento tecnico, tanto semplice da enunciare quanto utile: lo switch lemma, che dice quando è lecito scambiare l'ordine di due lub annidati.

Lemma (switch). Sia E una CPO e {en,m}n,m∈ℕ una famiglia doppiamente indicizzata, monotona in entrambi gli indici: en,m⊑en′,m′sen≤n′∧m≤m′ Allora ogni riga, ogni colonna e la diagonale sono catene, e i tre modi di "saturare" la matrice coincidono: ⨆n∈ℕ⨆m∈ℕen,m=⨆m∈ℕ⨆n∈ℕen,m=⨆k∈ℕek,k

Idea della dimostrazione. Si prova che tutti questi insiemi hanno lo stesso insieme di maggioranti (per riga: ogni elemento di una riga sta sotto al lub di riga; per diagonale: ogni en,m sta sotto a ek,k con k=max{n,m}, perché n≤k e m≤k). Avendo gli stessi maggioranti, hanno lo stesso minimo dei maggioranti. ■

Intuitivamente: per calcolare il limite di una "matrice crescente" si può procedere per righe, per colonne o lungo la diagonale, ottenendo sempre lo stesso risultato.

Spazio delle funzioni

Date due CPO⊥ D ed E, il dominio funzionale [D→E] è l'insieme delle funzioni continue da D a E, non tutte le funzioni, solo quelle continue, ordinate punto a punto: f⊑[D→E]g⇔∀d∈D.f(d)⊑Eg(d)

Conviene avere in mente alcuni esempi su [ℤ⊥→ℤ⊥]. Due funzioni totali distinte sono sempre inconfrontabili (ogni funzione totale è massimale): per avere f⊑g con f≠g occorre che f sia indefinita (⊥) là dove g è definita, e che coincidano altrove. L'ordine ⊑[D→E] misura quindi quanto una funzione "raffina" l'altra estendendone il dominio di definizione.

Anche [D→E] è una CPO⊥:

1. Ogni catena di funzioni {fn} (anche non continue) ha come lub la funzione definita punto a punto h=λd.⨆n∈ℕfn(d)cioèh(d)=⨆nfn(d) che è un maggiorante ed è il minimo dei maggioranti (entrambe le verifiche si fanno punto a punto). 2. Se le fn sono continue, allora anche h lo è, ed è qui che interviene lo switch lemma. Data una catena {di} in D: h(⨆idi)=⨆nfn(⨆idi)=⨆n⨆ifn(di)=⨆i⨆nfn(di)=⨆ih(di) Lo scambio centrale dei due lub è lecito perché la famiglia en,i=fn(di) è monotona in entrambi gli indici: se n≤m e i≤j allora fn(di)⊑fn(dj)⊑fm(dj) (per continuità/monotonia di fn e per fn⊑fm).

Poiché il limite di una catena di funzioni continue è continuo, esso appartiene a [D→E], che risulta dunque chiuso e completo. Ricordiamo infine che la composizione di funzioni continue è continua, cioè se f∈[D→E] e g∈[E→F] allora g∘f∈[D→F].

Domini sollevati e somma disgiunta

Per completare la cassetta degli attrezzi servono altri due costruttori.

Il sollevamento (lifting) di una CPO D è D⊥={⊥}⊎D. Attenzione: non è l'ordine piatto. Il lifting aggiunge un nuovo bottom strettamente sotto a una copia fedele ⌊d⌋ di D, conservandone l'ordine interno: ⊥D⊥⊑x∀x;⌊d1⌋⊑D⊥⌊d2⌋⇔d1⊑Dd2 È una CPO⊥, con ⨆i⌊di⌋=⌊⨆idi⌋. L'operatore di lifting (⋅)*:[D→E]→[D⊥→E] trasforma f in f* con f*(⊥)=⊥E e f*(⌊d⌋)=f(d): è ben definito, monotono e continuo. Su di esso si basa la comoda notazione di de-lifting letx⇐t.e, che estrae il valore da t (se definito) e lo assegna a x in e, propagando altrimenti ⊥E.

La somma disgiunta D+E=(D⊎E,⊑) affianca i due domini tenendoli separati (tramite iniezioni continue ιD,ιE); serve a interpretare i tipi unione. La costruzione dell'ordine, del bottom e delle iniezioni è un utile esercizio.

Teoremi di continuità

Verificare la continuità "a mano" di ogni funzione semantica sarebbe poco pratico. I teoremi di continuità permettono di ridurre il problema a controlli più semplici, e forniscono un compendio di operatori già noti essere continui.

Continuità sul codominio prodotto

Teorema. Sia f:D→E1×E2 e poniamo gi=πi∘f. Allora f è continua se e solo se g1 e g2 lo sono.

La direzione (⇒) segue dal fatto che le proiezioni sono continue e la composizione di funzioni continue è continua. La direzione (⇐) si calcola componente per componente, usando che f(d)=(g1(d),g2(d)) e che il lub nelle coppie è componente per componente. In pratica: una funzione a valori in un prodotto è continua esattamente quando lo sono le sue componenti.

Continuità separata sugli argomenti

Teorema. Sia f:D×E→F. Definite le applicazioni parziali fd=λe.f(d,e):E→Ffe=λd.f(d,e):D→F allora f è continua se e solo se fd è continua per ogni d∈D e fe è continua per ogni e∈E.

Questo è uno dei risultati più usati: per stabilire la continuità di una funzione a due argomenti basta controllarla un argomento alla volta. La direzione delicata è (⇐): data una catena {(dk,ek)} in D×E, si separa il lub di coppie e si applica la continuità prima su un argomento e poi sull'altro, usando infine lo switch lemma per ricomporre il doppio lub f(⨆k(dk,ek))=⨆j⨆if(di,ej)=⨆kf(dk,ek) applicabile perché f(di,ej) è monotona in i e in j (le applicazioni parziali sono monotone).

Attenzione. Continuità (e perfino monotonia) separata va verificata con cura: una funzione può essere ben definita "come grafo" ma non monotona se "guarda" se un argomento è ⊥. Ad esempio g(x,y)=x⋅y se x,y≠⊥ e g(⊥,y)=0 non è nemmeno monotona, perché g(⊥,⊥)=0not⊑ valori prodotti quando gli argomenti si raffinano.

Operatori fondamentali

I teoremi precedenti permettono di dimostrare che i costituenti della semantica denotazionale sono continui:

In definitiva, i costruttori (prodotto, spazio funzionale, lifting, somma) sono chiusi rispetto alla struttura di CPO⊥, e gli operatori fondamentali sono continui: abbiamo così un linguaggio completo per costruire, in modo compositivo, i domini e le funzioni semantiche di un linguaggio funzionale di ordine superiore.

Articoli correlati